package dev.ideas.funkyj.group;

import dev.ideas.funkyj.function.F;
import dev.ideas.funkyj.sequence.Sequence;
import org.junit.Test;

import static dev.ideas.funkyj.sequence.Sequence.*;
import static org.junit.Assert.*;

/**
 */
public class GroupBy {

    @Test public void group() {
        assertEquals(
                from(range(1, 10)).groupBy(even).
                        select(true).to(sum).get(),
                (Integer) 30);

        assertEquals(
                from(range(1, 10)).where(even).groupBy(odd).
                        select(false).to(sum).get(),
                (Integer) 30);
    }

    @Test public void group1() {
        final Group1<Integer,Integer> group1 = from(range(0, 9)).groupBy(_1);
        final Sequence<Integer> sequence = group1.select(2);

        //assert: group should contain the path 2 -> 5, without values for now
        assertEquals(group1.map.keySet(), from(2).asSet());
        assertTrue(group1.map.get(2).cache.isEmpty());

        final Integer first = sequence.first();
        assertEquals(first, (Integer) 2);

        //assert: group should contain the path 0..2
        assertEquals(group1.map.keySet(), from(range(0, 2)).asSet());
        for (int i = 0; i <=2; i++) assertEquals(group1.map.get(i).cache, from(i).asList());

        final Integer first2 = sequence.first();
        assertEquals(first2, first);

        //assert: group should not create new paths or values
        assertEquals(group1.map.keySet(), from(range(0, 2)).asSet());
        for (int i = 0; i <=2; i++) assertEquals(group1.map.get(i).cache, from(i).asList());

        for (Integer i : sequence) { }

        assertEquals(group1.map.keySet(), from(range(0, 9)).asSet());
        for (int i = 0; i <=9; i++)
            assertEquals(group1.map.get(i).cache, from(i).asList());
    }

    @Test public void group2() {
        final Group2<Integer,Integer,Integer> group2 = from(range(10, 99)).groupBy(_2, _1);
        final Sequence<Integer> sequence = group2.select(2).select(5);

        //assert: group should contain the path 2 -> 5, without values for now
        assertEquals(group2.map.keySet(), from(2).asSet());
        assertEquals(group2.map.get(2).map.keySet(), from(5).asSet());
        assertTrue(group2.map.get(2).map.get(5).cache.isEmpty());
        
        final Integer first = sequence.first();
        assertEquals(first, (Integer) 25);

        //assert: group should contain the path 1 -> {0 .. 9}, 2 -> {0 .. 5}, with value 25
        assertEquals(group2.map.keySet(), from(1, 2).asSet());
        assertEquals(group2.map.get(1).map.keySet(), from(range(0, 9)).asSet());
        assertEquals(group2.map.get(2).map.keySet(), from(range(0, 5)).asSet());
        for (int i = 10; i <= 25; i++) {
            assertEquals(group2.map.get(_2.of(i)).map.get(_1.of(i)).cache, from(i).asList());
        }

        final Integer first2 = sequence.first();
        assertEquals(first2, first);

        //assert: group should not create new paths or values
        assertEquals(group2.map.keySet(), from(1, 2).asSet());
        assertEquals(group2.map.get(1).map.keySet(), from(range(0, 9)).asSet());
        assertEquals(group2.map.get(2).map.keySet(), from(range(0, 5)).asSet());
        for (int i = 10; i <= 25; i++) {
            assertEquals(group2.map.get(_2.of(i)).map.get(_1.of(i)).cache, from(i).asList());
        }

        for (Integer i : sequence) { }

        assertEquals(group2.map.keySet(), from(range(1, 9)).asSet());
        for (int i = 10; i <= 99; i++) {
            assertEquals(group2.map.get(_2.of(i)).map.get(_1.of(i)).cache, from(i).asList());
        }
    }

    @Test public void group3() {
        final Group3<Integer, Integer, Integer, Integer> group3 = from(range(100, 999)).groupBy(_3, _2, _1);
        final Sequence<Integer> sequence = group3.select(2).select(5).select(7);

        //assert: group should contain the path 2 -> 5, without values for now
        assertEquals(group3.map.keySet(), from(2).asSet());
        assertEquals(group3.map.get(2).map.keySet(), from(5).asSet());
        assertEquals(group3.map.get(2).map.get(5).map.keySet(), from(7).asSet());
        assertTrue(group3.map.get(2).map.get(5).map.get(7).cache.isEmpty());

        final Integer first = sequence.first();
        assertEquals(first, (Integer) 257);

        for (Integer i : range(100, 999)) {
            try { assertEquals(group3.map.get(_3.of(i)).map.get(_2.of(i)).map.get(_1.of(i)).cache, (i > 257 ? from() : from(i)).asList()); }
            catch (Exception ex) { assertTrue(i > 257); }
        }

        final Integer first2 = sequence.first();
        assertEquals(first2, first);

        for (Integer i : range(100, 999)) {
            try { assertEquals(group3.map.get(_3.of(i)).map.get(_2.of(i)).map.get(_1.of(i)).cache, (i > 257 ? from() : from(i)).asList()); }
            catch (Exception ex) { assertTrue(i > 257); }
        }

        for (Integer i : sequence) { }

        for (Integer i : range(100, 999)) {
            assertEquals(group3.map.get(_3.of(i)).map.get(_2.of(i)).map.get(_1.of(i)).cache, from(i).asList());
        }
    }

    public static final F<Integer, Integer> shift = new F<Integer, Integer>() {
        @Override public Integer apply(Integer integer) {
            return integer / 10;
        }
    };
    public static final F<Integer, Integer> _1 = new F<Integer, Integer>() {
        @Override public Integer apply(Integer integer) {
            return integer % 10;
        }
    };
    public static final F<Integer, Integer> _2 = _1.of(shift);
    public static final F<Integer, Integer> _3 = _2.of(shift);
}
